package com.iluwatar.layers;

import java.util.Arrays;

/**
 * 
 * Layers is an architectural style where software responsibilities are
 * divided among the different layers of the application.
 * <p>
 * This example demonstrates a traditional 3-layer architecture consisting of data access
 * layer, business layer and presentation layer.
 * <p>
 * The data access layer is formed of Spring Data repositories <code>CakeDao</code>, <code>CakeToppingDao</code> and
 * <code>CakeLayerDao</code>. The repositories can be used for CRUD operations on cakes, cake toppings
 * and cake layers respectively.
 * <p>
 * The business layer is built on top of the data access layer. <code>CakeBakingService</code> offers
 * methods to retrieve available cake toppings and cake layers and baked cakes. Also the
 * service is used to create new cakes out of cake toppings and cake layers.
 * <p>
 * The presentation layer is built on the business layer and in this example it simply lists
 * the cakes that have been baked.
 * <p>
 * We have applied so called strict layering which means that the layers can only access 
 * the classes directly beneath them. This leads the solution to create an additional set of
 * DTOs (<code>CakeInfo</code>, <code>CakeToppingInfo</code>, <code>CakeLayerInfo</code>) 
 * to translate data between layers. In other words, <code>CakeBakingService</code> cannot 
 * return entities (<code>Cake</code>, <code>CakeTopping</code>, <code>CakeLayer</code>) 
 * directly since these reside on data access layer but instead translates these into business 
 * layer DTOs (<code>CakeInfo</code>, <code>CakeToppingInfo</code>, <code>CakeLayerInfo</code>) 
 * and returns them instead. This way the presentation layer does not have any knowledge of 
 * other layers than the business layer and thus is not affected by changes to them.
 *
 * @see Cake
 * @see CakeTopping
 * @see CakeLayer
 * @see CakeDao
 * @see CakeToppingDao
 * @see CakeLayerDao
 * @see CakeBakingService
 * @see CakeInfo
 * @see CakeToppingInfo
 * @see CakeLayerInfo
 *
 */
public class App {

	private static CakeBakingService cakeBakingService = new CakeBakingServiceImpl();
	
	/**
	 * Application entry point
	 * @param args Command line parameters
	 */
	public static void main(String[] args) {
		
		// initialize example data
		initializeData(cakeBakingService);
		
		// create view and render it
		CakeViewImpl cakeView = new CakeViewImpl(cakeBakingService);
		cakeView.render();
	}
	
	/**
	 * Initializes the example data
	 * @param cakeBakingService
	 */
	private static void initializeData(CakeBakingService cakeBakingService) {
		cakeBakingService.saveNewLayer(new CakeLayerInfo("chocolate", 1200));
		cakeBakingService.saveNewLayer(new CakeLayerInfo("banana", 900));
		cakeBakingService.saveNewLayer(new CakeLayerInfo("strawberry", 950));
		cakeBakingService.saveNewLayer(new CakeLayerInfo("lemon", 950));
		cakeBakingService.saveNewLayer(new CakeLayerInfo("vanilla", 950));
		cakeBakingService.saveNewLayer(new CakeLayerInfo("strawberry", 950));
		
		cakeBakingService.saveNewTopping(new CakeToppingInfo("candies", 350));
		cakeBakingService.saveNewTopping(new CakeToppingInfo("cherry", 350));

		CakeInfo cake1 = new CakeInfo(new CakeToppingInfo("candies", 0),
				Arrays.asList(new CakeLayerInfo("chocolate", 0), new CakeLayerInfo("banana", 0),
						new CakeLayerInfo("strawberry", 0)));
		try {
			cakeBakingService.bakeNewCake(cake1);
		} catch (CakeBakingException e) {
			e.printStackTrace();
		}
		CakeInfo cake2 = new CakeInfo(new CakeToppingInfo("cherry", 0),
				Arrays.asList(new CakeLayerInfo("vanilla", 0), new CakeLayerInfo("lemon", 0),
						new CakeLayerInfo("strawberry", 0)));
		try {
			cakeBakingService.bakeNewCake(cake2);
		} catch (CakeBakingException e) {
			e.printStackTrace();
		}
	}
}
